package com.huike.Ascept;

import com.huike.clues.domain.SysOperLog;
import com.huike.clues.mapper.SysoperLogMapper;
import com.huike.common.core.domain.model.LoginUser;

import com.huike.common.utils.ServletUtils;
import com.huike.common.utils.ip.AddressUtils;
import com.huike.common.utils.spring.SpringUtils;
import com.huike.framework.web.service.TokenService;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * AOP操作日志记录切面
 * @Author zhu
 * @Date 2025/10/27 17:23
 */
@Aspect
@Component
@Slf4j
public class LogAscept {

    @Autowired
    private TaskExecutor taskExecutor;

    @Autowired
    private SysoperLogMapper sysoperLogMapper;

    @Around("@annotation(com.huike.log.logOperation)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("进入操作日志切面");

        // 记录开始时间
        long startTime = System.currentTimeMillis();

        Object result = null;
        String errorMsg = null;
        boolean success = true;

        try {
            // 执行目标方法
            result = joinPoint.proceed();
            return result;
        } catch (Exception e) {
            success = false;
            errorMsg = e.getMessage();
            throw e;
        } finally {
            // 计算执行时间
            long endTime = System.currentTimeMillis();

            // 异步记录日志
            final boolean finalSuccess = success;
            final String finalErrorMsg = errorMsg;
            final Object finalResult = result;

            taskExecutor.execute(() -> {
                try {
                    recordLog(joinPoint, finalSuccess, finalErrorMsg, finalResult, endTime - startTime);
                } catch (Exception e) {
                    log.error("记录操作日志失败", e);
                }
            });
        }
    }

  private void recordLog(ProceedingJoinPoint joinPoint, boolean success, String errorMsg, Object result, long executeTime) {
    try {
        SysOperLog operLog = new SysOperLog();

        // 获取当前HTTP请求信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();

            // 设置请求相关信息
            operLog.setOperUrl(request.getRequestURI());
            operLog.setRequestMethod(request.getMethod()); // 确保请求方法被正确记录
            String ip = getClientIpAddress(request);
            operLog.setOperIp(ip);

            // 根据IP地址获取地理位置
            String address = AddressUtils.getRealAddressByIP(ip);
            operLog.setOperLocation(address != null ? address : "未知");

            // 设置操作时间
            operLog.setOperTime(new Date());

            // 获取当前登录用户信息
            try {
                TokenService tokenService = SpringUtils.getBean(TokenService.class);
                LoginUser loginUser = tokenService.getLoginUser(request);
                if (loginUser != null && loginUser.getUser() != null) {
                    operLog.setOperName(loginUser.getUsername());
                    // 如果需要设置部门名称，可以从loginUser.getUser()中获取
                    // operLog.setDeptName(loginUser.getUser().getDeptName());
                } else {
                    operLog.setOperName("未知用户");
                }
            } catch (Exception e) {
                operLog.setOperName("未知用户");
                log.warn("获取登录用户信息失败", e);
            }
        } else {
            // 如果无法获取请求信息，设置默认值
            operLog.setOperName("未知用户");
            operLog.setOperTime(new Date());
        }

        // 设置方法相关信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        operLog.setMethod(method.getDeclaringClass().getName() + "." + method.getName());

        // 设置请求参数
        Object[] args = joinPoint.getArgs();
        StringBuilder paramBuilder = new StringBuilder();
        for (Object arg : args) {
            if (arg != null && !(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)) {
                paramBuilder.append(arg.toString()).append(";");
            }
        }
        operLog.setOperParam(paramBuilder.length() > 0 ? paramBuilder.toString() : "");

        // 设置返回结果
        if (result != null) {
            operLog.setJsonResult(result.toString());
        }

        // 设置操作状态
        operLog.setStatus(success ? 0 : 1);
        operLog.setErrorMsg(errorMsg != null ? errorMsg : "");

        // 设置操作类别（可以根据实际情况判断是后台还是移动端）
        operLog.setOperatorType(1); // 默认为后台用户

        // 设置业务类型（需要从注解中获取）
        com.huike.log.logOperation logAnnotation = method.getAnnotation(com.huike.log.logOperation.class);
        if (logAnnotation != null) {
            operLog.setTitle(logAnnotation.title());  // 正确访问注解的title属性
            operLog.setBusinessType(logAnnotation.businessType());  // 正确访问注解的businessType属性
        } else {
            // 如果没有注解，设置默认值
            operLog.setTitle("未知操作");
            operLog.setBusinessType(0);
        }

        // 插入日志记录
        sysoperLogMapper.insert(operLog);

        log.info("操作日志记录成功: {}", operLog.getTitle());
    } catch (Exception e) {
        log.error("记录操作日志异常", e);
    }
}


    /**
     * 获取客户端真实IP地址
     */
    private String getClientIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}
